﻿/*
    Copyright (c) 2008 - 2009, Carlos Guzmán Álvarez

    All rights reserved.

    Redistribution and use in source and binary forms, with or without modification, 
    are permitted provided that the following conditions are met:

        * Redistributions of source code must retain the above copyright notice, 
          this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright notice, 
          this list of conditions and the following disclaimer in the documentation and/or 
          other materials provided with the distribution.
        * Neither the name of the author nor the names of its contributors may be used to endorse or 
          promote products derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Framework.Net.Xmpp.Core;
using Framework.Net.Xmpp.InstantMessaging;
using Microsoft.Practices.Composite.Presentation.Commands;
using Microsoft.Practices.Unity;
using XClient.Commands;
using XClient.Configuration;
using XClient.Infrastructure;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Markup;

namespace XClient.ViewModels
{
    /// <summary>
    /// View model for chat views
    /// </summary>
    /// <remarks>
    /// Bring paragraphs into view seeing here:
    /// http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/858200c8-6dc8-4a26-a1a3-9bbe7b6ea106
    /// </remarks>
    public class ChatViewModel : ViewModel<object>
    {
        #region · Fields ·

        private IUnityContainer     container;
        private XmppChat            chat;
        private FlowDocument        conversation;
        private ChatCommandsProxy   commandProxy;
        private List<Emoticon>      emoticonPackage;

        #endregion

        #region · Properties ·

        /// <summary>
        /// Gets the header info
        /// </summary>
        public override string HeaderInfo
        {
            get { return this.chat.Contact.DisplayName; }
        }

        /// <summary>
        /// Gets the chat conversation as a <see cref="FlowDocument"/>
        /// </summary>
        public FlowDocument Conversation
        {
            get { return this.conversation; }
        }

        /// <summary>
        /// Gets the chat contact information
        /// </summary>
        public XmppContact Contact
        {
            get { return this.chat.Contact; }
        }

        /// <summary>
        /// Gets the emoticon list
        /// </summary>
        public List<Emoticon> EmoticonPackage
        {
            get { return this.emoticonPackage; }
        }

        #endregion

        #region · Command Properties ·

        /// <summary>
        /// Gets or sets the add new command.
        /// </summary>
        /// <value>The add new command.</value>
        public DelegateCommand<object> CloseCommand
        {
            get;
            private set;
        }

        #endregion

        #region · Constructors ·

        public ChatViewModel(IUnityContainer container, XmppChat chat)
            : base()
        {
            this.container          = container;
            this.chat               = chat;
            this.emoticonPackage    = this.ResolveEmoticonPackage();

            this.conversation           = new FlowDocument();
            this.chat.ChatClosed        += new EventHandler(chat_ChatClosed);
            this.chat.ReceivedMessage   += new EventHandler(chat_ReceivedMessage);

            this.ShowMessages();

            this.InitializeCommands();
            this.RegisterCommands();
        }

        #endregion

        #region · Methods ·

        public void SendMessage(string message)
        {
            this.chat.SendMessage(message);
            this.ComposeOutgoingMessage(message);
        }

        #endregion

        #region · Command Actions ·

        /// <summary>
        /// Gets a value that indicates if the view can be closed
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        protected virtual bool CanClose(object obj)
        {
            return this.IsActive;
        }

        /// <summary>
        /// Closes the view
        /// </summary>
        /// <param name="obj"></param>
        protected virtual void Close(object obj)
        {
            string jid = this.chat.Contact.ContactId.BareIdentifier;

            this.chat.Close();
            this.container.Resolve<ChatViewManager>().CloseChatView(jid);
        }

        #endregion

        #region · Command Methods ·

        /// <summary>
        /// Initializes the commands.
        /// </summary>
        protected override void InitializeCommands()
        {
            this.commandProxy = new ChatCommandsProxy();
            this.CloseCommand = new DelegateCommand<object>(Close, CanClose);
        }

        /// <summary>
        /// Registers the commands.
        /// </summary>
        protected override void RegisterCommands()
        {
            this.commandProxy.CloseChatCommand.RegisterCommand(this.CloseCommand);
        }

        /// <summary>
        /// Activates the commands
        /// </summary>
        protected override void ActivateCommands()
        {
            this.CloseCommand.IsActive = this.IsActive;
        }

        #endregion

        #region · Private Methods ·

        private List<Emoticon> ResolveEmoticonPackage()
        {
            ArrayList list = (ArrayList)XamlReader.Load(Application.GetResourceStream(new Uri("pack://application:,,,/Themes/Emoticons.xaml")).Stream);
            List<Emoticon> package = new List<Emoticon>();

            foreach (Emoticon emo in list)
            {
                package.Add(emo);
            }

            return package;
        }

        private void ShowMessages()
        {
            if (this.chat != null)
            {
                this.Dispatcher.BeginInvoke(
                     DispatcherPriority.Normal,
                     new ThreadStart(delegate
                     {
                         while (this.chat.PendingMessages.Count > 0)
                         {
                             XmppMessage currentMessage = this.chat.PendingMessages.Dequeue();

                             if (!String.IsNullOrEmpty(currentMessage.Body))
                             {
                                 this.ComposeIncomingMessage(currentMessage.Body);
                             }
                         }
                     }));
            }
        }

        private void ComposeOutgoingMessage(string message)
        {
            Paragraph paragraph = new Paragraph();

            // Paragraph settings
            paragraph.FontFamily    = new FontFamily("Segoe UI");
            paragraph.FontSize      = 10;

            paragraph.Inlines.Add(new Bold(new Run(DateTime.Now.ToShortTimeString())));
            paragraph.Inlines.Add(new Bold(new Run(" ")));
            paragraph.Inlines.Add(new Bold(new Run(this.container.Resolve<ConfigurationManager>().SelectedAccount.DisplayName)));
            paragraph.Inlines.Add(new LineBreak());
            paragraph.Inlines.Add(new Run(message));

            this.conversation.Blocks.Add(paragraph);

            Action<Paragraph> x = r => r.BringIntoView();
            this.Dispatcher.BeginInvoke(x, DispatcherPriority.SystemIdle, paragraph);

            this.NotifyPropertyChanged("Conversation");
        }

        private void ComposeIncomingMessage(string message)
        {
            Paragraph   paragraph       = new Paragraph();
            BitmapImage avatarPicture   = new BitmapImage();
            Image       avatarControl   = new Image();

            // Paragraph settings
            paragraph.FontFamily = new FontFamily("Segoe UI");
            paragraph.FontSize = 10;

            /*
            // Avatar picture
            avatarPicture.BeginInit();
            avatarPicture.UriSource = new Uri(this.chat.Contact.Resource.OnlineAvatarPath, UriKind.Absolute);
            avatarPicture.EndInit();
            
            // Avatr control
            avatarControl.Height    = avatarPicture.PixelHeight;
            avatarControl.Width     = avatarPicture.PixelWidth;

            avatarControl.Source = avatarPicture;
            */
            
            // Paragraph composition
            paragraph.Inlines.Add(new Bold(new Run(DateTime.Now.ToShortTimeString())));
            paragraph.Inlines.Add(new Bold(new Run(" ")));
            paragraph.Inlines.Add(new Bold(new Run(this.chat.Contact.DisplayName)));
            paragraph.Inlines.Add(new LineBreak());
            paragraph.Inlines.Add(new Run(message));

            this.conversation.Blocks.Add(paragraph);

            Action<Paragraph> x = r => r.BringIntoView();
            this.Dispatcher.BeginInvoke(x, DispatcherPriority.SystemIdle, paragraph);

            this.NotifyPropertyChanged("Conversation");
        }

        #endregion

        #region · Event Handlers ·

        private void chat_ReceivedMessage(object sender, EventArgs e)
        {
            this.ShowMessages();
        }

        private void chat_ChatClosed(object sender, EventArgs e)
        {
            // Unbind event handlers
            this.chat.ChatClosed        -= new EventHandler(chat_ChatClosed);
            this.chat.ReceivedMessage   -= new EventHandler(chat_ReceivedMessage);
            this.chat                   = null;
        }

        #endregion
    }
}
